#pragma once

#include <iostream>
#include <string>
#include <cstdint>
#include <thread>

#include <sys/wait.h>
#include <unistd.h>

#include "Socket.hpp"


using namespace std;
using namespace SocketModule;

class TcpServer
{
    static const uint16_t default_port = 0;
    static const uint16_t default_read_size=4096;

    typedef function<void(const string&)> ssh_handle_t;

    void HandleRequeat(SockPtr sock) // 处理请求
    {
        while (true)
        {
            // 读取请求
            string in;
            int n = sock->Recv(in);
            if (n > 0) // 读取成功
            {
                LOG(LogLevel::DEBUG) << "client# " << in;

                // 处理请求--ssh
                int fd[2];
                pipe(fd);//创建管道
                int ret=fork();//创建子进程
                if(ret==0)
                {
                    close(fd[0]);//关闭读端

                    dup2(fd[1],1);//输出重定向到管道

                    _ssh(in);//执行命令

                    exit(0);
                }
                close(fd[1]);//关闭写端
                waitpid(ret,nullptr,0);//回收子进程

                char rbuf[default_read_size];

                string out;
                while(true)
                {
                    int rsize=read(fd[0],rbuf,default_read_size);

                    if(rsize>0)
                    {
                        rbuf[rsize]='\0';
                        out+=rbuf;
                    }
                    else if(rsize==0)//写端关闭时，若管道为空，则读端读取到结束标志
                    {
                        break;
                    }
                }
                if(out=="")
                {
                    out="the command does not exist\n";
                }
                sock->Send(out);
            }
            else if (n == 0) // 连接断开
            {
                LOG(LogLevel::DEBUG) << "客户端退出";
                sock->Close();
                break;
            }
            else
            {
                LOG(LogLevel::ERROR) << "recv error";
            }
        }
    }

public:
    TcpServer(uint16_t port = default_port)
        : _port(port),
          _listensocket(make_shared<TcpSocket>(port)),
          _isrunning(false),
          _ssh(nullptr)
    {
    }
    void InitServer()
    {
        // 创建lisentsocket
        _listensocket->BuildSocket(SockType::LISTEN_SOCK);
    }
    void Registration(ssh_handle_t ssh)//注册
    {
        _ssh=ssh;
    }
    void Start()
    {

        _mutex.lock();          // 保护_isrunning
        if (_isrunning == true) // 不允许重复启动
        {
            return;
        }
        _isrunning = true;

        while (_isrunning)
        {
            _mutex.unlock();
            // 主线程负责获取新连接
            uint32_t client_ip;
            uint16_t client_port;
            SockPtr sock = _listensocket->Accept(client_ip, client_port);
            if (sock == nullptr) // 获取新连接失败
            {
                continue;
            }

            // 连接管理--多线程版本
            thread t([this,sock]()
            {
                this->HandleRequeat(sock);
            });
            t.detach();//线程分离

            //---------------------------//

            // 连接管理--多进程版本
            /*int n = fork();
            if (n == 0) // 子进程
            {
                // 创建孙子进程，并让孙子进程变成孤儿进程
                // 这样就不需要主动等待回收了
                if (fork() > 0)
                {
                    // 子进程退出
                    exit(0);
                }

                // 孙子进程去处理请求
                HandleRequeat(sock);
                // 孙子进程退出
                exit(0);
            }
            // 回收退出的子进程
            sock->Close(); // 父进程关闭fd，防止fd泄漏

            int r = waitpid(n, nullptr, 0);
            if (r < 0)
            {
                LOG(LogLevel::ERROR) << "waitpid error";
            }*/
        }
    }
    void Stop()
    {
        lock_guard<mutex> lock(_mutex); // 保护_isrunning
        _isrunning = false;
    }
    ~TcpServer()
    {
        _listensocket->Close();
    }

private:
    uint16_t _port;
    SockPtr _listensocket;
    bool _isrunning;
    ssh_handle_t _ssh;
    mutex _mutex;
};